// const _ = require('lodash')

// const array = ['xm', 'xq', 'xh']

// console.log(_.head(array), _.first(array)) // xm xm
// console.log(_.last(array)) // xh
// console.log(_.toUpper(_.first(array))) // XM
// console.log(_.reverse(array)) // ['xh', 'xq', 'xm']



// const _ = require('lodash')
// function getArea (r) {
//   return Math.PI * r * r
// }
// let getAreaFromMemory = _.memoize(getArea)
// console.log(getAreaFromMemory(4))
// console.log(getAreaFromMemory(4))


// const _ = require('lodash')
// function getSum (a, b, c) {
//   return a + b + c
// } 
// let curried = curry(getSum)
// console.log(curried(1, 2, 3)) // 6
// console.log(curried(1)(2)(3)) // 6
// console.log(curried(1, 2)(3)) // 6

// function curry(func){
//   return function curried(...args){
//     if(args.length<func.length){
//       return function () {
//         return curried(...args.concat(Array.from(arguments)))
//       }
//     }
//     return func(...args)
//   }
// }


// 把数组最后一个元素转换为大写
// const _ = require('lodash')
// const toUpper = s => s.toUpperCase()
// const reverse = arr => arr[0].reverse()
// const first = arr => arr[0]
// const f = _.flowRight(toUpper, first, reverse)
// console.log(f(['one', 'two', 'three']))


// function flowRight (...args) {
//   return function () {
//     let res = []
//     args.reverse()
//     for (let i = 0; i < args.length; i++) {
//       if (i === 0) {
//         res = args[i](...arguments)
//       } else {
//         res = args[i](res)
//       }
//     }
//     return res
//   }
// }

// function flowRight (...args) {
//   return function (value) {
//     return args.reverse().reduce((acc, fn) => {
//       return fn(acc)
//     }, value)
//   }
// }
// const flowRight = (...args) => value => args.reverse().reduce((acc, fn) => fn(acc), value)
// const toUpper = s => s.toUpperCase()
// const reverse = arr => arr.reverse()
// const first = arr => arr[0]
// const f = flowRight(toUpper, flowRight(first, reverse))
// console.log(f(['one', 'two', 'three']))


// const _ = require('lodash')
// // 柯里化，可以先输入 target，等待函数执行时，自动输出结果 v
// const trace = _.curry((tag, v) => {
//   console.log(tag, v)
//   return v
// })
// const split = _.curry((sep, str) => _.split(str, sep))
// const join = _.curry((sep, array) => _.join(array, sep))
// const map = _.curry((fn, array) => _.map(array, fn))
// const f = _.flowRight( trace('join 之后') ,join('-'), trace('map 之后'), map(_.toLower), trace('split之后'), split(' '))
// console.log(f('NEVER SAY DIE'))

// const _ = require('lodash')

// let arr = ['23','8','11']
// console.log( _.map(arr,parseInt)) // 输出：[ 23, NaN, 3 ]
// // 分析：
// //  map 的回调函数需要接收三个参数，value, index|key, collection
// //  而 parseInt 的第二个参数是进制，取值是 2~36
// //  第一次调用：parseInt('23',0,arr) 0 的话，为十进制，也就是 23
// //  第二次调用：parseInt('8',1,arr) 不支持 1，也就是 NaN
// //  最后一次：parseInt('11',2,arr) 二进制转 10 进制，也就是 3
// //  满足条件，需要自己定义一个 parseInt，只接受一个参数

// // lodash fp 模块

// const fp = require('lodash/fp')
// // map 的函数只接收一个参数
// console.log(fp.map(parseInt,['23','8','11']))


// const fp = require('lodash/fp')
// const f = fp.flowRight(fp.replace(/\s+/g, '_'), fp.toLower)
// console.log(f('Hello World'))


// 一个容器，包裹一个值
// class Container {
//   constructor (value) {
//     // 函子内部要有一个值，不对外公布
//     this._value = value
//   }
//   // map 方法，接收处理值的函数，返回一个新的函子
//   map (fn) {
//     return  Container.of(fn(this._value))
//   }
//   static of(value){
//     return new Container(value)
//   }
// }
// const res =  Container.of(3)
//   .map(x => x + 2)
//   .map(x => x * x)
// console.log(res)

// class MayBe {
//   static of (value) {
//     return new MayBe(value)
//   } 
//   constructor (value) {
//     this._value = value
//   }
//   map (fn) {
//     return this.isNothing() ? MayBe.of(null) :MayBe.of(fn(this._value))
//   } 
//   isNothing () {
//     return this._value === null || this._value === undefined
//   }
// }
// // 传入具体值
// MayBe.of('Hello World')
//   .map(x => x.toUpperCase())
// // 传入 null 的情况
// MayBe.of(null)
//   .map(x => x.toUpperCase())
//   // => MayBe { _value: null }



// class Left {
//   static of (value) {
//     return new Left(value)
//   }
//   constructor (value) {
//     this._value = value
//   }
//   map (fn) {
//     return this
//   }
// }
// class Right {
//   static of (value) {
//     return new Right(value)
//   }
//   constructor (value) {
//     this._value = value
//   }
//   map(fn) {
//     return Right.of(fn(this._value))
//   }
// }
// const res1 = Left.of(12).map(x => x+2)
// const res2 = Right.of(12).map(x => x+2)
// console.log(res1,res2) // Left { _value: 12 } Right { _value: 14 }


// const fp = require('lodash/fp')
// const process = require('process')
// class IO {
//   static of (x) {
//     return new IO(function () {
//       return x
//     })
//   }
//   constructor(fn) {
//     this._value = fn
//   }
//   map (fn) {
//     return new IO(fp.flowRight(fn, this._value))
//   }
// }
// let io = IO.of(process).map(p => p.execPath)
// console.log(io._value())


// const { task } = require('folktale/concurrency/task')
// const {split,find} = require('lodash/fp')
// const fs = require('fs')
// function readFile (filename) {
//   return task(resolver => {
//     fs.readFile(filename, 'utf-8', (err, data) => {
//       if (err) {
//         resolver.reject(err)
//       }
//       resolver.resolve(data)
//     })
//   })
// }
// // 调用 run 执行
// readFile('package.json')
//   .map(split('\n'))
//   .map(find(x => x.includes('version')))
//   .run().listen({
//     // 失败
//     onRejected: err => {
//       console.log(err)
//     },
//     // 成功
//     onResolved: value => {
//       console.log(value)
//     }
//   })
// const { compose, curry } = require('folktale/core/lambda')
// const { toUpper, first } = require('lodash/fp')
// // 第一个参数是传入函数的参数个数
// let f1 = curry(2, function (x, y) {
//   console.log(x + y)
// })
// f1(3, 4)
// f1(3)(4)
// // 函数组合
// let f2 = compose(toUpper, first)
// console.log(f2(['one', 'two']))



// const fs = require('fs')
// const fp = require('lodash/fp')

// class IO {
//   // of 接收数据
//   static of (x) {
//     return new IO(function () {
//       return x
//     })
//   }
//   constructor(fn) {
//     this._value = fn
//   }
//   map (fn) {
//     return new IO(fp.flowRight(fn, this._value))
//   }
// }
// let readFile = function (filename) {
//   return new IO(function () {
//     return fs.readFileSync(filename, 'utf-8')
//   })
// }
// let print = function (x) {
//   return new IO(function () {
//     console.log(x)
//     return x
//   })
// }
// // IO(IO(x))
// let cat = fp.flowRight(print, readFile)
// // 调用
// let r = cat('package.json')._value()._value()
// console.log(r)


const fp = require('lodash/fp')
const fs = require('fs')
// IO Monad
class IO {
  static of (x) {
    return new IO(function () {
      return x
    })
  }
  constructor(fn) {
    this._value = fn
  }
  map (fn) {
    return new IO(fp.flowRight(fn, this._value))
  }
  join () {
    return this._value()
  }
  flatMap (fn) {
    return this.map(fn).join()
  }
}
let readFile = function (filename) {
  return new IO(function () {
    return fs.readFileSync(filename, 'utf-8')
  })
}
let print = function (x) {
  return new IO(function () {
    console.log(x)
    return x
  })
}
let res = readFile('package.json')
  .map(fp.toUpper)
  .flatMap(print)
  .join()
console.log(res)